"""Script for generating a CSV file suitable for uploading to Google AppEngine
from UnicodeData.txt.

Resources:
http://theory.uwinnipeg.ca/CPAN/perl/lib/Unicode/UCD.html
http://www.ksu.ru/eng/departments/ktk/test/perl/lib/unicode/UCDFF301.html
"""
import csv
import textwrap
import unittest

import fieldnames
import chardata
import util

def _transformCharData(line, mapping=fieldnames.mapping):
  """Takes a dictionary read from UnicodeData.txt and adds more data to it via
  the mapping object.
  """
  newline = dict(line)
  for (name, fn) in mapping:
    newline[name] = fn(newline)
  return newline

def _getReader(filename="UnicodeData.txt"):
  datafile = util.opendatafile(filename, "r")
  wrapper = UnicodeDataFileWrapper(datafile)
  return csv.DictReader(wrapper, fieldnames=fieldnames.source, delimiter=";",
                        quotechar='"')

def _getWriter(filename="UnicodeChars.csv"):
  outputfile = util.opendatafile(filename, "w")
  return csv.DictWriter(outputfile, fieldnames=fieldnames.destination,
                        delimiter=",", quotechar='"', lineterminator="\n")

class UnicodeDataFileWrapper(object):
  """A wrapper class that implements the file interface to exclude all control
  characters (named "<control>" in UnicodeData.txt).
  """
  def __init__(self, file):
    self._file = file
  
  def __iter__(self):
    for line in self._file:
      if self.shouldSkip(line):
        continue
      yield line
  
  @classmethod
  def shouldSkip(cls, line):
    return "<control>" in line

def main():
  """Creates a CSV file suitable for upload to AppEngine from UnicodeData.txt.
  """
  print("Creating CSV...")
  chardata.initData()
  reader = _getReader()
  writer = _getWriter()
  completed = []
  for line in reader:
    line = _transformCharData(line)
    writer.writerow(line)
    completed.append(line)
  print("Done. Wrote %i lines." % len(completed))

class GenerateCsvTestCase(unittest.TestCase):
  class MockFile(object):
    """A read-write mock file object.
    """
    def __init__(self, content=""):
      self._content = content
    
    def __iter__(self):
      for line in self._content.split("\n"):
        yield line
    
    def write(self, stuff):
      self._content += stuff
    
    def __str__(self):
      return self._content
  
  def testGenerateCsv(self):
    chardata.initData()
    content = textwrap.dedent("""
      0023;NUMBER SIGN;Po;0;ET;;;;;N;;;;;
      0026;AMPERSAND;Po;0;ON;;;;;N;;;;;
      002E;FULL STOP;Po;0;CS;;;;;N;PERIOD;;;;
      2212;MINUS SIGN;Sm;0;ES;;;;;N;;;;;
      2328;KEYBOARD;So;0;ON;;;;;N;;;;;
      0027;APOSTROPHE;Po;0;ON;;;;;N;APOSTROPHE-QUOTE;;;;
      0028;LEFT PARENTHESIS;Ps;0;ON;;;;;Y;OPENING PARENTHESIS;;;;
      """).strip()
    reader = csv.DictReader(self.MockFile(content),
                            fieldnames=fieldnames.source, delimiter=";",
                            quotechar='"')
    outputfile = self.MockFile("")
    writer = csv.DictWriter(outputfile, fieldnames=fieldnames.destination,
                            delimiter=",", quotechar='"', lineterminator="\n")
    for line in reader:
      line = _transformCharData(line)
      writer.writerow(line)
    output = str(outputfile)
    self.assertTrue("&amp;" in output)
    self.assertTrue("&minus;" in output)
    self.assertTrue("Math Symbol" in output)
    self.assertTrue("Other Punctuation" in output)
    self.assertTrue("Number Sign" in output)
    self.assertTrue("Apostrophe-Quote" in output)
    self.assertTrue("Opening Parenthesis" in output)
    self.assertTrue("38,Ampersand" in output)
  
  def testCsvWriter(self):
    d = {"name": "Bob", "id": 1234}
    outputfile = self.MockFile("")
    writer = csv.DictWriter(outputfile, fieldnames=["id", "name"],
                            delimiter=",", quotechar='"', lineterminator="\n")
    writer.writerow(d)
    pieces = str(outputfile).split("\n")[0].split(",")
    self.assertTrue("1234" in pieces)
    self.assertTrue("Bob" in pieces)
  
  def testShouldSkip(self):
    line = """001F;<control>;Cc;0;S;;;;;N;INFORMATION SEPARATOR ONE;;;;"""
    self.assertTrue(UnicodeDataFileWrapper.shouldSkip(line))
    line = """0020;SPACE;Zs;0;WS;;;;;N;;;;;"""
    self.assertFalse(UnicodeDataFileWrapper.shouldSkip(line))
  
  def testTransformCharData(self):
    line = {"name": "NAME"}
    mapping = [("name", lambda d: d["name"] + "2")]
    newline = _transformCharData(line, mapping)
    self.assertEqual(newline["name"], "NAME2")

if __name__ == "__main__":
  main()
